/*
    This source file is part of GeNa project.
    Copyright © GeNa project 2008
    For the latest info, see http://code.google.com/p/genaproject/ or  http://lumpyproject.forums-rpg.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>. */

#include <string>
#include <vector>

#include <boost/config.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/version.hpp>


#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/fstream.hpp>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>

using namespace std;
using namespace boost::filesystem;
using namespace boost::archive;

#include "map_manager.h"
#include "path.h"
#include "application.h"
#include "map_serialization.h"
#include "action.h"

Map* MapManager::load( string filename, path ph)
{

    std::string ext = extension(path(filename));

    if( ext == "")
    {
        Map* map = load( filename + ".map", ph);
        if( map)
            return map;
        return load( filename + ".map.xml", ph);
    }

    path p = complete(path(filename), ph);

    if( !exists(p))
    {
        //Try the default path
        ph = system_complete(pathListInstance.triatekMapPath);
        if( !exists(ph))
        {
            wxLogError(_("Le chemin %s du dossier des cartes du jeu est introuvable."), (const wchar_t*)wxConvUTF8.cMB2WC(pathListInstance.triatekMapPath.string().c_str()));
            wxLogError(_("Impossible de trouver la carte %s."), (const wchar_t*)wxConvUTF8.cMB2WC(filename.c_str()));
            return NULL;
        }

        p = complete( path(filename), ph);

        if( !exists(p))
        {
            wxLogError(_("Impossible de trouver la carte %s."), (const wchar_t*)wxConvUTF8.cMB2WC(filename.c_str()));
            return NULL;
        }
    }

    //Search if the file is opened
    for( unsigned int i=0; i<m_mapList.size(); i++)
    {
        if( boost::filesystem::equivalent( m_mapList[i]->mapPath, p))
        {
            wxLogError(_("La carte %s est déjà ouverte"), (const wchar_t*)wxConvUTF8.cMB2WC(filename.c_str()));
            return m_mapList[i];
        }
    }


    //Now, open the file and read it
    boost::filesystem::ifstream ifs(p);

    if( !ifs.good())
    {
        wxLogError(_("Impossible d’ouvrir %s."), (const wchar_t*)wxConvUTF8.cMB2WC(filename.c_str()));
        return NULL;
    }

    Map* map = new Map;
    map->filename = boost::filesystem::path(filename).filename();
    map->mapPath = p;

    try
    {
        if( extension(p) == ".xml")
        {
            xml_iarchive ia(ifs);
            ia >> make_nvp("map",*map);
        }
        else
        {
            text_iarchive ia(ifs);
            ia >> make_nvp("map",*map);
        }
    }
    catch(...)
    {
        ifs.close();
        wxLogError(_("Échec de la lecture de %s (syntaxe ou contenu incorrecte)."), (const wchar_t*)wxConvUTF8.cMB2WC(filename.c_str()));
        return NULL;
    }
    ifs.close();

    m_mapList.push_back(map);
    map->loadWindowsManager();
    return map;
}


void MapManager::save(Map* map)
{
    if( map->filename == "")
    {
        saveAs(map);
        return;
    }

    //Now, write in the file
    boost::filesystem::ofstream ofs(map->mapPath);

    if( !ofs.good())
        return;

    try
    {
        if( extension(map->mapPath) == ".xml")
        {
            boost::archive::xml_oarchive oa(ofs);
            oa << make_nvp("map",*map);
        }
        else
        {
            boost::archive::text_oarchive oa(ofs);
            oa << make_nvp("map",*map);
        }
    }
    catch(...)
    {
        ofs.close();
        return;
    }
    ofs.close();
}

void MapManager::saveAs(Map* map)
{
    wxFileDialog* dlg = new wxFileDialog(appInstance->m_frame, wxT("Enregistrer sous …"), wxString(pathListInstance.triatekMapPath.string().c_str(),wxConvUTF8), wxT("") , wxT("*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

    if( dlg->ShowModal() == wxID_OK)
    {
        map->mapPath = path(std::string(dlg->GetPath().mb_str()));
        map->filename = map->mapPath.filename();
        save(map);
    }

    if( map == current)
        setCurrent(map); //Actualize the frame title
}

void MapManager::open()
{
    wxFileDialog* dlg = new wxFileDialog(appInstance->m_frame, wxT("Ouvrir"), wxString(pathListInstance.triatekMapPath.string().c_str(),wxConvUTF8), wxT("") , wxT("*.*"), wxFD_OPEN);

    if( dlg->ShowModal() == wxID_OK)
    {
        std::string file = std::string(dlg->GetPath().mb_str());
        Map* map = NULL;
        if( file.length())
        {
            map = mapManagerInstance.load(file,boost::filesystem::system_complete(pathListInstance.triatekMapPath));
            wxLogMessage(_("Chargement de %s"), file.c_str());
            if( map)
                wxLogMessage(_("Chargement de %s réussi"), file.c_str());
            else
                wxLogError(_("Échec du chargement de %s"), file.c_str());
        }

        if( map)
        {
            mapManagerInstance.setCurrent(map);
            appInstance->m_frame->m_auiNotebook->SetSelection(appInstance->m_frame->m_auiNotebook->GetPageIndex(map->mgr->getParent()));
        }
    }
}

Map* MapManager::newMap()
{
    Map* map = new Map;
    m_mapList.push_back(map);
    map->loadWindowsManager();
    return map;
}

void MapManager::setCurrent(Map* map)
{
    current = map;

    int p = appInstance->m_frame->m_auiNotebook->GetPageIndex(map->mgr->getParent());

    if( map->filename != "")
    {
        appInstance->m_frame->SetTitle( wxString(map->filename.c_str(), wxConvUTF8) + _(" - Éditeur Triatek"));
        appInstance->m_frame->m_auiNotebook->SetPageText(p,wxString(map->filename.c_str(), wxConvUTF8));
    }
    else
    {
        appInstance->m_frame->SetTitle(_(" Nouvelle carte - Éditeur Triatek"));
        appInstance->m_frame->m_auiNotebook->SetPageText(p,_("Nouvelle carte"));
    }

    if( map->actionMgr->canUndo())
    {
        appInstance->m_frame->m_standard->EnableTool(MainFrame::idMenuUndo, true);
        appInstance->m_frame->m_editMenu->Enable(MainFrame::idMenuUndo, true);
    }
    else
    {
        appInstance->m_frame->m_standard->EnableTool(MainFrame::idMenuUndo, false);
        appInstance->m_frame->m_editMenu->Enable(MainFrame::idMenuUndo, false);
    }

    if( map->actionMgr->canRedo())
    {
        appInstance->m_frame->m_standard->EnableTool(MainFrame::idMenuRedo, true);
        appInstance->m_frame->m_editMenu->Enable(MainFrame::idMenuRedo, true);
    }
    else
    {
        appInstance->m_frame->m_standard->EnableTool(MainFrame::idMenuRedo, false);
        appInstance->m_frame->m_editMenu->Enable(MainFrame::idMenuRedo, false);
    }
}

void MapManager::autoSave()
{
    path base("./autosave");
    try
    {
        if(exists(base))
            remove_all( base);
        create_directory(base);
    }
    catch(...)
    {
    }

    for( unsigned int i = 0; i < m_mapList.size(); i++)
    {
        std::string c;
        if( i <= 9)
            c[0] =  i + '0';
        else if( i <= 35)
            c[0] = i + 'a';
        else if( i <= 60)
            c[0] = i + 'A';
        else
            break; //Likely to never happen.

        if( !m_mapList[i]->filename.length())
        {
            m_mapList[i]->filename = "a"; //Avoid that save function call saveAs function
            m_mapList[i]->mapPath = base/path( c + ".map");
            save( m_mapList[i]);
            m_mapList[i]->mapPath = path();
            m_mapList[i]->filename = "";
        }
        else
        {
            path p = m_mapList[i]->mapPath;
            m_mapList[i]->mapPath = base/path( c + m_mapList[i]->filename);
            save( m_mapList[i]);
            m_mapList[i]->mapPath = p;
        }
    }
}
